/* ----------------------------- Noble Steed Engine----------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 5 2013

File:
	nsmath.h

Description:
	This file contains various types and math calcs to make the 3d life easier
*---------------------------------------------------------------------------*/

#ifndef NSMATH3D_H
#define NSMATH3D_H

#define MATDIM3D 3
#define MATDIM4D 4

#include <string>
// Vector type

struct NSVec2Df
{
	NSVec2Df();
	NSVec2Df(const NSVec2Df & copy);
	NSVec2Df(float _u, float _v);
	float u;
	float v;
	float & x;
	float & y;
	std::string toString();
	NSVec2Df operator+(const NSVec2Df & rhsVector) const;
	NSVec2Df operator-(const NSVec2Df & rhsVector) const;
	NSVec2Df & operator=(const NSVec2Df & rhsVector);
	NSVec2Df & operator+=(const NSVec2Df & rhsVector);
	NSVec2Df & operator-=(const NSVec2Df & rhsVector);
	bool operator==(const NSVec2Df & rhsVector) const;
	bool operator!=(const NSVec2Df & rhsVector) const;

	NSVec2Df operator*(const float rhsScalar) const;
	NSVec2Df operator/(const float rhsScalar) const;
	NSVec2Df & operator*=(const float rhsScalar);
	NSVec2Df & operator/=(const float rhsScalar);
};

struct NSVec3Df 
{
	NSVec3Df();
	NSVec3Df(const NSVec3Df & xyz);
	NSVec3Df(const NSVec2Df & xy, float _z);
	NSVec3Df(float _x, const NSVec2Df & yz);
	NSVec3Df(float _x, float _y, float _z);

	float x;
	float y;
	float z;

	// Normalize i, j, and k to be of length 1 (while retaining their direction)
	void normalize();

	float length();

	// Rotate over the axis (uses matrix transform)
	void rotate(const NSVec3Df & amount);
	void rotate(float xAxisAng, float yAxisAng, float zAxisAng);

	// Add to the vector - use negative values to subtract something
	void add(float _x, float _y,float _z);
	void set(float _x, float _y, float _z);

	NSVec3Df cross(const NSVec3Df & crossedWith) const;
	float angleFrom(const NSVec3Df & otherVec) const;
	std::string toString();

	// overloaded operators
	NSVec3Df operator+(const NSVec3Df & rhsVector) const;
	NSVec3Df operator-(const NSVec3Df & rhsVector) const;
	NSVec3Df & operator=(const NSVec3Df & rhsVector);
	NSVec3Df & operator+=(const NSVec3Df & rhsVector);
	NSVec3Df & operator-=(const NSVec3Df & rhsVector);
	bool operator==(const NSVec3Df & rhsVector) const;
	bool operator!=(const NSVec3Df & rhsVector) const;

	float operator*(const NSVec3Df & rhsVector);  // return dot product

	NSVec3Df operator*(const float rhsScalar) const;
	NSVec3Df operator/(const float rhsScalar) const;
	NSVec3Df & operator*=(const float rhsScalar);
	NSVec3Df & operator/=(const float rhsScalar);
};

// Vector type meant to hold vectors pointing from origin
struct NSVec4Df 
{
	NSVec4Df();
	NSVec4Df(const NSVec4Df & xyzw);

	NSVec4Df(const NSVec3Df & xyz, float _w);
	NSVec4Df(float _x, const NSVec3Df & yzw);

	NSVec4Df(const NSVec2Df & xy, const NSVec2Df & zw);
	NSVec4Df(const NSVec2Df & xy, float _z, float _w);
	NSVec4Df(float _x, const NSVec2Df & yz, float _w);
	NSVec4Df(float _x, float _y, const NSVec2Df & zw);

	NSVec4Df(float _x, float _y, float _z, float _w);

	float x;
	float y;
	float z;
	float w;

	// Normalize i, j, and k to be of length 1 (while retaining their direction)
	void normalize();

	float length();

	// Rotate over the axis (uses matrix transform)
	void rotate(const NSVec3Df & amount);
	void rotate(float xAxisAng, float yAxisAng, float zAxisAng);

	// Add to the vector - use negative values to subtract something
	// supports adding 3d vectors - basically just leaves w alone then
	void add(float _x,  float _y,  float _z,  float _w);
	void set(float _x, float _y, float _z, float _w);


	float angleFrom(const NSVec4Df & otherVec) const; // only compares first three elements
	std::string toString();
	// overloaded operators
	NSVec4Df operator+(const NSVec4Df & rhsVector) const;
	NSVec4Df operator-(const NSVec4Df & rhsVector) const;
	NSVec4Df & operator=(const NSVec4Df & rhsVector);
	NSVec4Df & operator+=(const NSVec4Df & rhsVector);
	NSVec4Df & operator-=(const NSVec4Df & rhsVector);
	bool operator==(const NSVec4Df & rhsVector) const;
	bool operator!=(const NSVec4Df & rhsVector) const;

	float operator*(const NSVec4Df & rhsVector) const;  // return dot product

	NSVec4Df operator*(const float & rhsScalar) const;
	NSVec4Df operator/(float rhsScalar) const;
	NSVec4Df & operator*=(float rhsScalar);
	NSVec4Df & operator/=(float rhsScalar);
};


class NSMatrix3Df
{
public:
	NSMatrix3Df(); // initialize to identity
	NSMatrix3Df(const NSMatrix3Df & copy);
	NSMatrix3Df(NSVec3Df vec1, NSVec3Df vec2, NSVec3Df vec3);

	// Will normalize every vector in 
	void normalizeAll();
	void setData(const NSVec3Df & row1,const NSVec3Df & row2,const NSVec3Df & row3);
	void setRow(const NSVec3Df & vec, int row);
	void setColumn(const NSVec3Df & vec, int column);

	float & value(int row, int column); // get and set values
	const float & value(int row, int column) const;

	NSVec3Df getRow(int row) const;
	NSVec3Df getColumn(int column) const;
	const float * getDataPtr();

	// Overloaded operators
	NSMatrix3Df operator*(const NSMatrix3Df & rhsMatrix) const;
	NSMatrix3Df operator+(const NSMatrix3Df & rhsMatrix) const;
	NSMatrix3Df operator-(const NSMatrix3Df & rhsMatrix) const;
	// less than and greater than not supported
	bool operator==(const NSMatrix3Df & rhsMatrix) const;
	bool operator!=(const NSMatrix3Df & rhsMatrix) const;
	NSMatrix3Df & operator=(const NSMatrix3Df & rhsMatrix);
	NSMatrix3Df & operator+=(const NSMatrix3Df & rhsMatrix);
	NSMatrix3Df & operator-=(const NSMatrix3Df & rhsMatrix);
	NSMatrix3Df & operator*=(const NSMatrix3Df & rhsMatrix);

	NSMatrix3Df operator*(float rhsScalar) const;
	NSMatrix3Df operator/(float rhsScalar) const;

	// Overloaded for 3D vector
	NSVec3Df operator*(const NSVec3Df & rhsVec) const;

private:
	float data[3][3];
	const int rows;
	const int columns;
};

class NSMatrix4Df
{
public:
	NSMatrix4Df(); // initialize to identity matrix
	NSMatrix4Df(const NSMatrix4Df & copy);
	NSMatrix4Df(const NSVec4Df & vec1, const NSVec4Df & vec2, const NSVec4Df & vec3, const NSVec4Df & vec4);

	void normalizeAll();
	void setData(const NSVec4Df & vec1,const NSVec4Df & vec2,const NSVec4Df & vec3, const NSVec4Df & vec4);
	void setData(const NSVec3Df & vec1,const NSVec3Df & vec2,const NSVec3Df & vec3);
	void setRow(const NSVec4Df & vec, int row);
	void setColumn(const NSVec4Df & vec, int column);

	const float & value(int row, int column) const; // can get and set values
	float & value(int row, int column);

	NSVec4Df getRow(int row) const;
	NSVec4Df getColumn(int column) const;
	float * getDataPtr();
	float getDeterminate() const;
	NSMatrix4Df getInverse() const;
	void rotate(const NSVec3Df & amount);
	void rotate(float xAxisAng, float yAxisAng, float zAxisAng);
	void translate(const NSVec3Df & amount);
	void translate(float xMove, float yMove, float zMove);
	void scale(const NSVec3Df & amount);
	void scale(float scaleX, float scaleY, float scaleZ);
	std::string toString() const;

	// static functions
	static NSMatrix4Df getRotationMatrix(const NSVec3Df & rotation);
	static NSMatrix4Df getRotationMatrix(float xAxisAng, float yAxisAng, float zAxisAng);
	static NSMatrix4Df getTranslationMatrix(const NSVec3Df & translation);
	static NSMatrix4Df getTranslationMatrix(float xMove, float yMove, float zMove);
	static NSMatrix4Df getScalingMatrix(const NSVec3Df & scaling);
	static NSMatrix4Df getScalingMatrix(float scaleX, float scaleY, float scaleZ);
	static NSMatrix4Df getProjectionMatrix(float fovAngle, int screenWidth, int screenHeight, float zNear, float zFar);

	// Overloaded operators
	NSMatrix4Df operator*(const NSMatrix4Df & rhsMatrix) const;
	NSMatrix4Df operator+(const NSMatrix4Df & rhsMatrix) const;
	NSMatrix4Df operator-(const NSMatrix4Df & rhsMatrix) const;
	// less than and greater than not supported
	bool operator==(const NSMatrix4Df & rhsMatrix) const;
	bool operator!=(const NSMatrix4Df & rhsMatrix) const;
	NSMatrix4Df & operator=(const NSMatrix4Df & rhsMatrix);
	NSMatrix4Df & operator+=(const NSMatrix4Df & rhsMatrix);
	NSMatrix4Df & operator-=(const NSMatrix4Df & rhsMatrix);
	NSMatrix4Df & operator*=(const NSMatrix4Df & rhsMatrix);


	// Overloaded for 4D vector
	NSVec4Df operator*(const NSVec4Df & rhsVec) const;
	NSVec3Df operator*(const NSVec3Df & rhsVec) const;

private:
	float data[4][4];
	const int rows;
	const int columns;
};

// Helper functions

float RadiansToDegrees(float radians);
float DegreesToRadians(float degrees);

#endif